﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Cofe.Core;
using Cofe.Core.Interfaces;
using Cofe.Core.Property;
using Cofe.Core.Utils;
using ExifLib;

namespace Cofe.Media
{
    public class ExifPropertyProvider : ImagePropertyProvider
    {



        #region Constructor

        internal ExifPropertyProvider(IPropertyHost propertyHost)
            : base(propertyHost)
        {
            _additionalSupportedProperties.AddRange(
                Cofe.Media.PropertyDefinitions.ExifPropDefinitions.GetSupportedProperties());

            _additionalSupportedProperties.Add(CofeMediaProperties.FullThumbnail);
        }


        #endregion

        #region Methods

        private Regex extractValue = new Regex(RegexPatterns.ExtractValueInBracketPattern);

        private void loadFromExif(ExifReader reader, Dictionary<object, PropertyPair> updateList)
        {
            object val;
            int[] rational; float[] rational1;
            
            var thumbnailBytes = reader.GetJpegThumbnailBytes();
            if (thumbnailBytes != null && thumbnailBytes.Length > 0)
                updateList.AddOrUpdate(CofeMediaProperties.FullThumbnail, PropertyPair.FromValue(CofeMediaProperties.FullThumbnail, thumbnailBytes));

            Func<ExifTags, ExifTags, double> getGpsPos = (posTag, posRefTag) =>
            {
                double[] pos;
                string posRef;
                if (!reader.GetTagValue(posTag, out pos) || !reader.GetTagValue(posRefTag, out posRef))
                    return 0;

                double retVal = pos[0] + (pos[1] / 60) + (pos[2] / 3600);

                if (posRef.Equals("S", StringComparison.CurrentCultureIgnoreCase) || posRef.Equals("W", StringComparison.CurrentCultureIgnoreCase))
                    retVal = retVal * -1;

                return retVal;
            };

            var checkProperties = Cofe.Media.PropertyDefinitions.ExifPropDefinitions.GetSupportedProperties().Union(
                new object[] { 
                    CofeMediaProperties.Width,
                    CofeMediaProperties.Height,
                });

            foreach (var prop in checkProperties)
            {
                try
                {
                    var exifTagAttrib = CofeServices.PropertyDictionary.AttributeHelper.FindAttribute<ExifTagAttribute>(prop);
                    if (exifTagAttrib != null)
                    {
                        #region loopEachTag
                        foreach (var tag in exifTagAttrib.ExifTag)
                            if (reader.GetTagValue(tag, out val))
                            {
                                switch ((int)prop)
                                {
                                    case (int)ExifProperties.ResolutionUnit:
                                    case (int)ExifProperties.ExposureProgram:
                                    case (int)ExifProperties.ISO:
                                    case (int)ExifProperties.Flash:
                                    case (int)ExifProperties.Orientation:
                                        val = (Int16)(UInt16)val;
                                        break;
                                    case (int)CofeMediaProperties.Width:
                                    case (int)CofeMediaProperties.Height:
                                        val = (Int32)(UInt32)val;
                                        break;
                                    case (int)ExifProperties.DateTime:
                                    case (int)ExifProperties.DateTimeDigitized:
                                    case (int)ExifProperties.DateTimeOriginal:
                                        val = DateTime.ParseExact((string)val, "yyyy:MM:dd HH:mm:ss", CultureInfo.InvariantCulture);
                                        break;
                                    case (int)ExifProperties.Aperture:                                    
                                        val = ConverterUtils.ToFloat(val.ToString());
                                        break;
                                    case (int)ExifProperties.FNumber:
                                    case (int)ExifProperties.FocalLength:
                                    case (int)ExifProperties.XResolution:
                                    case (int)ExifProperties.YResolution:                                    
                                        if (reader.GetTagValue(tag, out rational))
                                            val = rational[0];
                                        break;
                                    case (int)ExifProperties.ExposureTime:
                                        if (reader.GetTagValue(tag, out rational))
                                            val = ConverterUtils.ToInt32(rational[1].ToString());
                                        break;
                                }
                                if (val is string)
                                    val = ((string)val).Trim();

                                var pp = PropertyPair.FromValue(prop, val);

                                if (pp.IsValid)
                                    updateList.AddOrUpdate(prop, pp);

                                break;
                            }
                        #endregion
                    }
                    else
                        switch ((int)prop)
                        {
                            case (int)Cofe.Media.ExifProperties.GPSLatitude:
                                updateList.AddOrUpdate(prop, PropertyPair.FromValue(prop, 
                                    getGpsPos(ExifTags.GPSLatitude, ExifTags.GPSLatitudeRef)));
                                break;
                            case (int)Cofe.Media.ExifProperties.GPSLongitude:
                                updateList.AddOrUpdate(prop, PropertyPair.FromValue(prop,
                                    getGpsPos(ExifTags.GPSLongitude, ExifTags.GPSLongitudeRef)));
                                break;
                        }

                    

                }
                 

                catch (Exception ex)
                {
                    //Console.WriteLine(prop.ToString() + ":" + ex.ToString());
                }

            }

          
        }



        public override async Task<PropertyPair[]> GetPropertyAsync(object property)
        {
            if (property.Equals(CofeMediaProperties.FullThumbnail) ||
                Cofe.Media.PropertyDefinitions.ExifPropDefinitions.IsPropertySupported(property))
            {
                Dictionary<object, PropertyPair> retList = new Dictionary<object, PropertyPair>();

                try
                {
                    using (var stream = await Entity.OpenStreamAsync(true, false))
                    using (var reader = new ExifReader(stream.Stream))
                    {
                        object val;
                        bool supportExif = reader.GetTagValue(ExifTags.ExifVersion, out val);

                        if (supportExif)
                            loadFromExif(reader, retList);

                        if (!(retList.ContainsKey(property)))
                            loadFromImage(stream, retList);
                    }
                }
                catch (Exception ex)
                {
                    return null;
                }
                //Console.WriteLine(retList.Count().ToString() + "items");

                return retList.Values.ToArray();
            }

            return await base.GetPropertyAsync(property);
        }

        #endregion

        #region Data

        #endregion

        #region Public Properties

        #endregion



    }
}
